﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Jemgine.Engine
{
    [AttributeUsage(AttributeTargets.All)]
    public class TweakableAttribute : System.Attribute
    {
        public String TweakName;

        public TweakableAttribute(String TweakName) { this.TweakName = TweakName; }
    }

    public interface TweakableCollectionSearch
    {
        Object Search(Object Collection, String str);
    }

    [AttributeUsage(AttributeTargets.All)]
    public class TweakableCollectionAttribute : TweakableAttribute
    {
        public System.Type ComparerType;

        public TweakableCollectionAttribute(String TweakName, System.Type ComparerType)
            : base(TweakName)
        {
            this.ComparerType = ComparerType;
        }
    }
#if !XBOX
    public class Tweakable
    {

        public virtual void SetValue(String NewValue) { }
        public virtual String GetValue() { return String.Empty; }

        public static Tweakable FindTweakable(String Path, Object On)
        {
            String[] Tokens = Path.Split(new char[] { '.' }, StringSplitOptions.None);
            return _FindTweakable(Tokens, 0, On);
        }

        private static Tweakable _FindTweakable(String[] Tokens, int StartIndex, Object On)
        {
            if (StartIndex >= Tokens.Length) return null;
            System.Type OnType = On.GetType();
            bool IsLeaf = StartIndex == Tokens.Length - 1;

            var Fields = OnType.GetFields();
            foreach (var Field in Fields)
            {
                var Attribute = FindAttribute<TweakableAttribute>(Field);
                if (Attribute != null)
                {
                    if (String.Compare(Attribute.TweakName, Tokens[StartIndex], true) == 0)
                    {
                        if (Attribute is TweakableCollectionAttribute)
                        {
                            if (IsLeaf) return null; //Can't tweak Collections; only their members
                            Object CollectionSearcher =
                                System.Activator.CreateInstance(( Attribute as TweakableCollectionAttribute ).ComparerType);
                            var Searcher = CollectionSearcher as TweakableCollectionSearch;
                            if (Searcher == null) return null;
                            Object Child = Searcher.Search(Field.GetValue(On), Tokens[StartIndex + 1]);
                            if (Child == null) return null;
                            return _FindTweakable(Tokens, StartIndex + 2, Child);
                        }
                        else
                        {
                            if (IsLeaf)
                                return new TweakableField(Field, On);
                            else
                                return _FindTweakable(Tokens, StartIndex + 1, Field.GetValue(On));
                        }
                    }
                }
            }

            var Properties = OnType.GetProperties();
            foreach (var Property in Properties)
            {
                var Attribute = FindAttribute<TweakableAttribute>(Property);
                if (Attribute != null)
                {
                    if (Attribute is TweakableCollectionAttribute) continue;
                    if (String.Compare(Attribute.TweakName, Tokens[StartIndex], true) == 0)
                    {
                        if (IsLeaf)
                            return new TweakableProperty(Property, On);
                        else
                            return _FindTweakable(Tokens, StartIndex + 1, Property.GetValue(On, null));
                    }
                }
            }

            return null;
        }

        private static AttributeType FindAttribute<AttributeType>(System.Reflection.MemberInfo Member)
            where AttributeType : class
        {
            var Attributes = Member.GetCustomAttributes(false);
            foreach (var Attribute in Attributes)
                if (Attribute is AttributeType)
                    return Attribute as AttributeType;
            return null;
        }

    }

    public class TweakableProperty : Tweakable
    {
        System.Reflection.PropertyInfo Property;
        Object _object;

        public TweakableProperty(System.Reflection.PropertyInfo Property, Object _object)
        {
            this.Property = Property;
            this._object = _object;
        }

        public override string GetValue()
        {
            return System.Convert.ChangeType(Property.GetValue(_object, null), typeof(String)) as String;
        }

        public override void SetValue(string NewValue)
        {
            Property.SetValue(_object, System.Convert.ChangeType(NewValue, Property.PropertyType), null);
        }
    }

    public class TweakableField : Tweakable
    {
        System.Reflection.FieldInfo Field;
        Object _object;

        public TweakableField(System.Reflection.FieldInfo Field, Object _object)
        {
            this.Field = Field;
            this._object = _object;
        }

        public override string GetValue()
        {
            return System.Convert.ChangeType(Field.GetValue(_object), typeof(String)) as String;
        }

        public override void SetValue(string NewValue)
        {
            Field.SetValue(_object, System.Convert.ChangeType(NewValue, Field.FieldType));
        }

    }
#endif
}